/*
 * Diccionario.cpp
 *
 *  Created on: 04/11/2010
 *      Author: julfc
 */

#include "Diccionario.h"

Diccionario::Diccionario() {
	string nombreArchivo = "ListaDiccionario.dat";
	strcpy(this->archivoLista , nombreArchivo.c_str());

	string nombreArchivoArbol = "ArbolDiccionario.dat";
	strcpy(this->archivoArbol, nombreArchivoArbol.c_str());

	string nombreArchivoNor = "ArbolNormas.dat";
	strcpy(this->archivoNormas , nombreArchivoNor.c_str());
}

ItemDiccionario Diccionario::obtenerVectorPalabrasMail(string cadena, long idMail) {
	bool end = false;
	unsigned int pos = 0;
	vector<PalabraPeso> vectPalabraPeso;

	vector<string> stopWords = obtenerStopWords();
	vector<PalabraPos> palabras = splitSinStopWords(cadena, " ");
	//Ordeno las palabras para calcular el peso mas facil.
//	if (idMail == 41) {
//		for (unsigned int i = 0; i < palabras.size(); i++) {
//			cout << palabras[i].palabra << " " << palabras[i].pos << endl;
//		}
//	}

	//FIXME Ver si anda bien el sort...
	sort(palabras.begin(), palabras.end());

	while (!end) {
		PalabraPeso palabraPeso = crearPalabraPeso(palabras, pos);
		vectPalabraPeso.push_back(palabraPeso);
		pos += palabraPeso.peso;

		if (pos >= palabras.size()) {
			end = true;
		}
	}
//	for (unsigned int i = 0; i < vectPalabraPeso.size(); i++) {
//		cout << vectPalabraPeso[i].palabra << " " << vectPalabraPeso[i].peso << " " << vectPalabraPeso[i].pos[0] << endl;
//		//cout << vectPalabraPeso[i].palabra << " " << vectPalabraPeso[i].peso << " " << endl;
//	}
	ItemDiccionario item = ItemDiccionario(idMail, vectPalabraPeso);
	return item;
}

bool Diccionario::insertarEnArbol(ItemDiccionario registro) {

	ArbolBSharp arbolNorma;
	arbolNorma.Abrir(this->archivoNormas);
	long norma = calcularNorma(registro);
	RegistroBSharp regNorma(registro.idMail, norma);
	arbolNorma.InsertarRegistro(regNorma);
	IdListaDiccionario *manejadorIdLista = IdListaSingleton<IdListaDiccionario>::GetInstance();
	ArbolBSharpFrontCoding arbol;
	arbol.Abrir(this->archivoArbol);
	ManejadorListaPalabras lista(this->archivoLista, ABRIR);
	long idLista = 0;

	if (registro.listaPalabras.size() == 0) {
		return false;
	}
	for (unsigned int i = 0; i < registro.listaPalabras.size(); i++) {

		RegistroBSharpStringLong regArbol = arbol.ObtenerRegistro(registro.listaPalabras[i].palabra);
		long nroBloque = 0;

		IdMailPeso mailPeso(registro.idMail, registro.listaPalabras[i].peso, registro.listaPalabras[i].pos);
		//El registro no existe, lo tengo que agregar.
		if (regArbol.valor == -1) {
			//idLista = lista.getCantidadListas();
			//idLista++;
			idLista = manejadorIdLista->getIdLista();
			idLista++;
			nroBloque = lista.escribirIdMailPeso(idLista, mailPeso);
			string clave = registro.listaPalabras[i].palabra;
			long valorExtra = idLista;

			RegistroBSharpStringLong regArbolAux(clave, nroBloque, valorExtra);
			arbol.InsertarRegistro(regArbolAux);

			//Actualizo el id de la lista
			//lista.setCantidadListas(idLista);
			manejadorIdLista->actualizarId();
		} else {
			//Hay que actualizar la lista de palabras.
			nroBloque = regArbol.valor;
			idLista = regArbol.valorExtra;
			long nroBloqueModif = lista.actualizarLista(regArbol.valorExtra, regArbol.valor, mailPeso);
			//Cambio el numero de bloque en el que estaba la lista.
			if (nroBloque != nroBloqueModif) {
				arbol.ModificarRegistro(regArbol.clave, nroBloqueModif, regArbol.valorExtra);
				regArbol.valor = nroBloqueModif;
			}
			/*
			if(registro.listaPalabras[i].palabra.compare("EUPHORIA") == 0) {
				cout << endl << "Encontre EUPHORIA en el mail" << registro.idMail << " nro lista: " << idLista << endl;
			}
			*/
		}
	}
	return true;
}

PalabraPeso Diccionario::crearPalabraPeso(vector<PalabraPos> palabras, int pos) {
	int peso = 1;
	string palabraInsertar = palabras[pos].palabra;
	//PalabraPeso resp(palabraInsertar);
	vector<long> posPalabra;
	posPalabra.push_back(palabras[pos].pos);
	for (unsigned int i = pos + 1; i < palabras.size(); i++) {
		if (palabras[i].palabra.compare(palabraInsertar) == 0) {
			posPalabra.push_back(palabras[i].pos);
			peso++;
		} else {
			break;
		}
	}
	PalabraPeso resp(palabraInsertar, peso, posPalabra);
	return resp;
}

bool Diccionario::esStopWord(vector<string> stopWords, string palabra) {
	bool resp = false;

	if (palabra.size() > 64 || palabra.size() < 2) {
		return true;
	}

	vector<string> characters = this->obtenerStopCharacters();
	for (unsigned int chars = 0; chars < characters.size(); chars++) {
	  if (palabra.find_first_of(characters[chars]) != palabra.npos) {
	    resp = true;
	    break;
	  }
	}

	if (palabra.compare("\t") == 0) {
		resp = true;
	}

	for (unsigned int i = 0; i < stopWords.size(); i++) {
		if (palabra.compare(stopWords[i]) == 0) {
			resp = true;
			break;
		}
	}
//	if (resp) {
//		cout << endl << "Se elimino: " << "|" << palabra << "|" << endl;
//	}
	return resp;
}

unsigned int Diccionario::obtenerPosProximaPalabra(vector<string> palabras, int posActual) {

	Parser util;
	string palabra = palabras[posActual];
	util.upper(palabra);
	unsigned int posPalabra = 0;
	for (unsigned int i = posActual + 1; i < palabras.size(); i++) {
		string aux = palabras[i];
		util.upper(aux);
		if (aux.compare(palabra) != 0) {
			posPalabra = i;
			break;
		}
	}
	if (posPalabra == 0) {
		return palabras.size();
	}
	return posPalabra;
}

vector<string> Diccionario::obtenerStopWords() {
  const string PATH_ARCHIVO_STWS = "listaStopWords.txt";
  Parser util;
  // Vector con todas las stop words a devolver por este metodo.
  vector<string> stopWords;
  fstream archivoStopWords;
  archivoStopWords.open(PATH_ARCHIVO_STWS.c_str(), std::fstream::in | std::fstream::out);
  if (!archivoStopWords.is_open()) {
    throw std::ios_base::failure("El archivo no pudo ser abierto.");
  } else {
    string cadena = "";
    while (leerStopWord(cadena, archivoStopWords)) {
      util.upper(cadena);
      stopWords.push_back(cadena);
    }
  }
  archivoStopWords.close();
  return stopWords;
}

bool Diccionario::leerStopWord(string &cadena, fstream &archivoStopWords) {
  char linea[MAX_LENGTH];

  archivoStopWords.getline((char*) &linea, MAX_LENGTH, '\n');
  cadena = linea;

  if (archivoStopWords.fail()) {
    archivoStopWords.clear();
    return false;
  }
  return true;
}

vector<string> Diccionario::obtenerStopCharacters() {
  vector<string> stopCharacters;

  // Incrementar <const>maxChars</const> al agregar una cadena o caracter en
  // <const>characters</const>.
  const int maxChars = 13;
  const string characters[maxChars] = {"\t", "<", ">", "*", "-", "\r", "#", "$", "=", "@", "#", "^", "&"};
  for (int i = 0; i < maxChars; i++) {
    stopCharacters.push_back(characters[i]);
  }
  return stopCharacters;
}

bool Diccionario::crearArchivos() {
	ManejadorListaPalabras listaPalabras(this->archivoLista, CREAR);
	listaPalabras.inicCantidadListas();
	//IdListaDiccionario *manejadorIdLista = Singleton2<IdListaDiccionario>::GetInstance();
	ArbolBSharpFrontCoding arbolDiccionario;
	arbolDiccionario.Crear(this->archivoArbol);
	ArbolBSharp arbolNormas;
	arbolNormas.Crear(this->archivoNormas);
	return true;
}

vector<PalabraPos> Diccionario::splitSinStopWords(string str, string delimitador){

	vector<string> stopWords = obtenerStopWords();
	str = limpiarSaltosDeLinea(str);
	unsigned int pos;
	Parser util;
	vector<PalabraPos> resultado;
	long posEnMail = 1;
	while((pos = str.find_first_of(delimitador)) != str.npos ) {
		if(pos > 0){
			string palabra = str.substr(0,pos);
			string palabraLimpia = limpiarPalabra(palabra);
			util.upper(palabraLimpia);
			bool filtrar = esStopWord(stopWords, palabraLimpia);
			if (!filtrar) {
				PalabraPos palabraPos(palabraLimpia, posEnMail);
				resultado.push_back(palabraPos);
			}
		}
		str = str.substr(pos+1);
		posEnMail++;
	}
	if(str.length() > 0){
		util.upper(str);
		str = limpiarPalabra(str);
		bool filtrar = esStopWord(stopWords, str);
		if (!filtrar) {
			PalabraPos palabraPos(str, posEnMail);
			resultado.push_back(palabraPos);
		}
	}
	return resultado;
}

string Diccionario::limpiarPalabra(string str) {
	const string chars[22] = {",", ".", "!", "$", "%", "'", "\"", ";", "-", "_", "(", ")", ":", "|", "[", "]", "?", "¿", "\r", "\t", "\n", "\\"};
	string resp = str;

	for(int i = 0; i< 22; i++ ) {
		unsigned int pos = 0;
		while (pos != resp.npos) {
			pos = resp.find_first_of(chars[i]);
			if(pos != str.npos) {
				resp.replace(pos, 1,"");
			}
		}
	}
	return resp;
}

string Diccionario::limpiarSaltosDeLinea(string str) {
	const string chars[2] = {"\r", "\n"};
	string resp = str;

	for(int i = 0; i< 2; i++ ) {
		unsigned int pos = 0;
		while (pos != resp.npos) {
			pos = resp.find_first_of(chars[i]);
			if(pos != str.npos) {
				resp.replace(pos, 1," ");
			}
		}
	}
	return resp;
}

long Diccionario::calcularNorma(ItemDiccionario item) {
	long norma = 0;
	vector<PalabraPeso> pp = item.listaPalabras;
	for (unsigned int i = 0; i < pp.size(); i++) {
		long frec = pp[i].peso;
		if (frec > norma) {
			norma = frec;
		}
	}
	return norma;
}

vector<ModeloDocumento> Diccionario::buscarTerminos(vector<string> terminos) {
	vector<ModeloDocumento> result;
	result = buscarTermino(terminos[0]);
	if (terminos.size() > 1) {
		for (unsigned int i = 1; i < terminos.size(); i++) {
			vector<ModeloDocumento> aux = buscarTermino(terminos[i]);
			result = interserctarVectores(result, aux);
		}
	}
	sort(result.begin(), result.end());
	return result;
}

vector<ModeloDocumento> Diccionario::buscarFrase(vector<PalabraPos> terminos) {
	vector<ModeloFrase> docs;
	cout <<"La frase ingresada es: ";
	for (unsigned int chu = 0; chu < terminos.size(); chu++) {
		cout << terminos[chu].palabra << " ";
	}
	cout << endl;
	terminos = limpiarTerminos(terminos);
	cout << endl;
	cout << "La busqueda por frase sin stop words es: \n";
	for (unsigned int pala = 0; pala < terminos.size(); pala++) {
		cout <<"	"  << terminos[pala].palabra << " en la posicion: " << terminos[pala].pos << endl;
	}
	cout << endl;
	for (unsigned int i = 0; i < terminos.size(); i++) {
		string termino = terminos[i].palabra;
		long pos = terminos[i].pos;
		vector<ModeloDocumento> mod = buscarTermino(termino);
		if (mod.size() < 1) {
			//Una de las palabras no esta en el diccionario.
			vector<ModeloDocumento> nulo;
			return nulo;
		}
		ModeloFrase modFrase(termino, pos, mod);
		docs.push_back(modFrase);
	}
	vector<ModeloDocumento> resp = refinarBusqueda(docs);
	return resp;
}

vector<PalabraPos> Diccionario::limpiarTerminos(vector<PalabraPos> terminos) {
	vector<string> stopWords = obtenerStopWords();
	vector<PalabraPos> resp;
	for (unsigned int i = 0; i < terminos.size(); i++) {
		string palabra = limpiarPalabra(terminos[i].palabra);
		if (!esStopWord(stopWords, palabra)) {
			resp.push_back(terminos[i]);
		}
	}
	return resp;
}

vector<ModeloDocumento> Diccionario::refinarBusqueda(vector<ModeloFrase> terminos) {
	vector<ModeloDocumento> resp;

	//Obtengo el primer modelo doc.
	vector<ModeloDocumento> modDoc = terminos[0].ocurrencias;

	//Por cada mod doc, me fijo si esta el id del mail en los otros.
	//O sea si contiene tambien las otras palabras.
	for (unsigned int j = 0; j < modDoc.size(); j++) {
		bool todosContienenClave = true;
		for (unsigned int k = 1; k < terminos.size() && todosContienenClave; k++){
			vector<ModeloDocumento> modDocAuxInt = terminos[k].ocurrencias;
			long difPos = terminos[k].pos - terminos[0].pos;
			if (!contieneId(modDocAuxInt, modDoc[j], difPos)) {
				todosContienenClave = false;
			}
		}
		if (todosContienenClave) {
			resp.push_back(modDoc[j]);
		}
	}
	return resp;
}

bool Diccionario::contieneId(vector<ModeloDocumento> vect, ModeloDocumento modAnterior, long difPos) {
	for (unsigned int i = 0; i < vect.size(); i++) {
		if (vect[i].idMail == modAnterior.idMail) {
			//cout << "\n Id que esta en los dos mails: " << modAnterior.idMail << "\n";
			vector<long> posAux = vect[i].pos;
			vector<long> posAnt = modAnterior.pos;
			bool cumpleDif = compararVectPos(posAux, posAnt, difPos, modAnterior.idMail);
			if (cumpleDif) {
				//cout << "\n Id que Cumple las dif: " << modAnterior.idMail << "\n";
				return true;
			}
		}
	}
	return false;
}

bool Diccionario::compararVectPos(vector<long> posAux ,	vector<long> posAnt, long difPos, long idMail) {
	for (unsigned int i = 0; i < posAnt.size(); i++) {
		for (unsigned int j = 0; j < posAux.size(); j++) {
			long difAux = posAux[j] - posAnt[i];
			if (difAux == difPos) {
				return true;
			}
		}
	}
	return false;
}

void Diccionario::imprimirModeloFrase(ModeloFrase mod) {
	cout << endl << endl;
	cout << "Termino: " << mod.termino << " Pos:" << mod.pos << endl;
	cout <<	"	Ocurrencias: " << endl;
	for (unsigned int j = 0; j < mod.ocurrencias.size(); j++) {
		cout << "		IdMail " << mod.ocurrencias[j].idMail << endl;
		cout << "			Pos: ";
		for (unsigned int i = 0; i < mod.ocurrencias[j].pos.size(); i++) {
			cout << mod.ocurrencias[j].pos[i]<< " ";
		}
		cout << endl;
	}
	cout << endl << endl;
}

vector<ModeloDocumento> Diccionario::buscarTermino(string terminos) {
	vector<ModeloDocumento> modelos;
	Diccionario dic;
	ArbolBSharpFrontCoding arbolDic;
	arbolDic.Abrir(dic.archivoArbol);
	RegistroBSharpStringLong reg = arbolDic.ObtenerRegistro(terminos);

	if (reg.valor < 0) {
		//El termino no esta en el Diccionario.
		return modelos;
	}

	ManejadorListaPalabras mlp(this->archivoLista, ABRIR);
	vector<IdMailPeso> ids = mlp.leerLista(reg.valor, reg.valorExtra);

	long frecGlobal = ids.size();
	if (frecGlobal == 0) {
		return modelos;
	}
	double pesoGlobal = getPesoGlobal(frecGlobal);

	//Armo el vector con el idMail y el calculo loco del apunte.
	ArbolBSharp arbolNormas;
	arbolNormas.Abrir(this->archivoNormas);
	for (unsigned int i = 0; i < ids.size(); i++) {
		RegistroBSharp regNorma = arbolNormas.ObtenerRegistro(ids[i].idMail);
		long normaDoc = regNorma.valor;
		long idMail = ids[i].idMail;
		//Similitud
		double calc = ((ids[i].peso * pesoGlobal)/normaDoc) * pesoGlobal;
		ModeloDocumento mdoc(idMail, calc, ids[i].pos);
		modelos.push_back(mdoc);
	}

	return modelos;
}

vector<ModeloDocumento> Diccionario::interserctarVectores(vector<ModeloDocumento> v1, vector<ModeloDocumento> v2) {
	//Busco los repetidos y sumo la similitud.
	vector<ModeloDocumento> resp;
	for (unsigned int i = 0; i < v1.size(); i++) {
		for (unsigned int j = 0; j < v2.size(); j++) {
			if (v1[i].idMail == v2[j].idMail) {
				ModeloDocumento elem(v1[i].idMail, v1[i].calculoLoco + v2[j].calculoLoco);
				resp.push_back(elem);
				//Lo borro para optimizar la funcion.
				v2.erase(v2.begin() + j);
				break;
			}
		}
	}
	return resp;
}

double Diccionario::getPesoGlobal(long frecGlobal) {
	ManejadorConfiguracion mc;
	long cantMails = mc.obtenerCantidadMails();
	double primer = cantMails;
	double segundo = frecGlobal;
	double div = primer/segundo;
	double resp = log(div);
	return resp;
}

vector<string> Diccionario::mostrarDiccionario() {
	vector<string> palabras;
	ArbolBSharpFrontCoding arbol;
	arbol.Abrir(this->archivoArbol);
	palabras = arbol.RecorrerOrdenado();
	return palabras;
}

Diccionario::~Diccionario() {
	// TODO Auto-generated destructor stub
}
